package com.geekchique.runningbuddy.service;


import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.app.Activity;
import android.location.Location;
import android.os.Handler;

import com.geekchique.runningbuddy.provider.ActivityID;
import com.geekchique.runningbuddy.util.DBHelperImpl;
import com.geekchique.runningbuddy.model.Jog;

/********************************************************************************************
 * 
 * @author Justin Morgenthau
 *
 * RemoteStopClockServiceInterface is a singleton class which provides a common interface 
 * between the client application and the StopClock service.  
 * 
 * Each activity in the application should call the activityStarted() method within its
 * onStart() method, and call activityStopped() within its onStop method.  This allows 
 * RemoteStopClockServiceInterface to shut down the service as needed when all activities
 * have stopped.
 * 
 */

public class RemoteStopClockServiceInterface {
    /****************************
    * Private Methods & Members
    *****************************/
	
	private static final String TAG = RemoteStopClockServiceInterface.class.getName();

	private static RemoteStopClockServiceInterface instance = null;
	
	private int activityCount;

	private Handler updateHandler = null;
	
	private Activity currentActivity;
	private Activity boundActivity;
	
	//NOTE: startTime and clockRunning are maintained both in the client and in the service.  This is done for the benefit
	//of runtime performance, but requires care that the values do not get out of sync.
	private long startTime = 0;
	private long stopTime = 0;
	private boolean clockRunning = false;

	private DBHelperImpl dbHelper;
	private Jog currentJog = null;
	
	//private constructor so nobody else creates an instance
	private RemoteStopClockServiceInterface()
	{
		activityCount = 0;

	}
	
	//messageService, messageConnection, and mCallback are used to communicate with the service
	private IRemoteStopClockService messageService;

	private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
		public void updatedStopwatch(String time) throws DeadObjectException {
			if (updateHandler!=null)
			{
				//Message msg = Message.obtain(updateHandler, ActivityID.UPDATESTOPCLOCK, time);
				//msg.sendToTarget();
			}
		}
		
		public void updatedRoute(Location newLocation)
		{
			Log.w(TAG, "Received location update in ServiceInterface " + newLocation.getLatitude() + ", " + newLocation.getLongitude());
			if (updateHandler!=null)
			{
				Message msg = Message.obtain(updateHandler, ActivityID.LOCATION_UPDATE, newLocation);
				msg.sendToTarget();
			}
		}
	
		
    };
    
    private ServiceConnection messageConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            messageService = IRemoteStopClockService.Stub.asInterface(service);
            try {
                messageService.registerStopClockUpdateListener(mCallback);
                startTime = messageService.getStartTime();
                stopTime = messageService.getStopTime();
                clockRunning = messageService.isStopClockRunning();
                if (clockRunning)
                {
                	populateCurrentJog();
                }
                else
                {
                	currentJog = null;
                }
                
            } catch (DeadObjectException e) {
            	Log.v(TAG, "Tried to register with dead Object");
            }

        }

        public void onServiceDisconnected(ComponentName className) {
            messageService = null;
        }

    };
	
    private void populateCurrentJog()
    {
    	long id = -666;
    	try {
    		id = messageService.getCurrentJogId();
    		currentJog = dbHelper.fetchFullJogEntry(Long.toString(id));
    	} catch (DeadObjectException e) {
    		Log.v(TAG, "Tried to fetch Jog Id with dead Object");
    	}
    	if (currentJog == null)
    	{
    		Log.w(TAG, "Could Not Get current jog with id:" + id);
    	}
    }

    
    
    /****************************
    * Public Static Methods
    *****************************/
    
	//getInstance() returns the singleton instance
	public static RemoteStopClockServiceInterface getInstance()
	{
		if (instance==null)
		{
			instance = new RemoteStopClockServiceInterface();
		}
		return instance;
	}


	/****************************
	* Public Methods
	*****************************/
	
	public void activityStarted(Activity activity)
	{
		if (activityCount==0)
		{
	    	dbHelper = new DBHelperImpl(activity);
			activity.startService(new Intent(activity, RemoteStopClockService.class), null);
			activity.bindService(new Intent(IRemoteStopClockService.class.getName()), messageConnection, Context.BIND_AUTO_CREATE);
			boundActivity = activity;
		}
		activityCount++;
		
		currentActivity = activity;
		
		Log.w(TAG, "activity count incremented to " + activityCount);
	}

	public void activityStopped(Activity activity)
	{
		activityCount--;
		Log.w(TAG, "activity count decremented to " + activityCount);
		if (activityCount==0)
		{
			try
			{
				messageService.unregisterStopClockUpdateListener(mCallback);

				//send service message to self-terminate if idle
				messageService.terminateServiceIfIdle();
				boundActivity.unbindService(messageConnection);
            } catch (DeadObjectException e) {
            	Log.v(TAG, "Tried to unregister with dead Object");
            }
		}
		
		if (currentActivity == activity) currentActivity = null;
	}
	
	public void startStopClock()
	{
		try {
			startTime = System.currentTimeMillis();
			stopTime = 0;
			Log.w(TAG, "Set startTime " + startTime);
			messageService.startStopClock(startTime);
			clockRunning = true;
        	populateCurrentJog();

        } catch (DeadObjectException e) {
        	Log.v(TAG, "Tried to start clock with dead Object");
        }
		if (currentActivity!=null)
		{
			currentActivity.broadcastIntent(new Intent(ActivityID.STOPWATCH_STARTED));
		}

	}

	public void stopStopClock()
	{
		try {
			stopTime = System.currentTimeMillis();
			messageService.stopStopClock(stopTime);
			clockRunning = false;
        } catch (DeadObjectException e) {
        	Log.v(TAG, "Tried to stop clock with dead Object");
        }
		if (currentActivity!=null)
		{
			currentActivity.broadcastIntent(new Intent(ActivityID.STOPWATCH_STOPPED));
		}
		currentJog = null;
	}

	public void setUpdateHandler(Handler handler)
	{
		updateHandler = handler;
	}
	
	public void clearUpdateHandler(Handler handler)
	{
		if (updateHandler == handler) updateHandler=null;
	}
	
    public synchronized String getElapsedTime() {

		Long elapsedTime;
		
		if (clockRunning)
		{
			elapsedTime = ( System.currentTimeMillis() - this.startTime );
		}
		else
		{
			if (stopTime > 0L)
			{
				elapsedTime = ( stopTime - this.startTime );
			}
			else
			{
				elapsedTime = 0L;
			}
		}

		long hours = elapsedTime / (1000 * 60 * 60);
		String hourString = Long.toString(hours);
		while (hourString.length()<2) hourString = "0" + hourString;
		elapsedTime = elapsedTime - (hours * 1000 * 60 * 60);
		
		long minutes = elapsedTime / (1000 * 60);
		String minuteString = Long.toString(minutes);
		while (minuteString.length()<2) minuteString = "0" + minuteString;
		elapsedTime = elapsedTime - (minutes * 1000 * 60);

		long seconds = elapsedTime / 1000;
		String secondsString = Long.toString(seconds);
		while (secondsString.length()<2) secondsString = "0" + secondsString;
		elapsedTime = elapsedTime - (seconds * 1000);

		String msString = Long.toString(elapsedTime / 10);
		while (msString.length()<2) msString = "0" + msString;

		return hourString + ":" + minuteString + ":" + secondsString + "." + msString ;
	}
    
    public boolean isClockRunning()
    {
    	return clockRunning;
    }
    
    public long getStartTime()
    {
    	return startTime;
    }
    
    public Jog getCurrentJog()
    {
    	populateCurrentJog();
    	return currentJog;
    }
    
    public void setCannedRouteEnabled(boolean enabled)
    {
    	try {
    		messageService.setCannedRouteEnabled(enabled);
    	}
    	catch (DeadObjectException e) {
    	}
    }
    
    public boolean getCannedRouteEnabled()
    {
    	try {
    		return messageService.getCannedRouteEnabled();
    	}
    	catch (DeadObjectException e) {
    	}
    	
    	return false;
    }
    
    public void setCannedRouteId(int id)
    {
    	try {
    		messageService.setCannedRouteId(id);
    	}
    	catch (DeadObjectException e) {
    	}
    }
    
    public int getCannedRouteId()
    {
    	try {
    		return messageService.getCannedRouteId();
    	}
    	catch (DeadObjectException e) {
    	}
    	
    	return 1;
    }

    public void setCannedRouteSpeed(double speed)
    {
    	try {
    		messageService.setCannedRouteSpeed(speed);
    	}
    	catch (DeadObjectException e) {
    	}
    }
    
    public double getCannedRouteSpeed()
    {
    	try {
    		return messageService.getCannedRouteSpeed();
    	}
    	catch (DeadObjectException e) {
    	}
    	
    	return 0.0;
    }

}
